package com.abpconsult.inputinjector; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import android.content.Context; import android.os.IBinder; import android.os.ServiceManager; import android.os.SystemClock; import android.util.Log; import android.view.IWindowManager; import android.view.InputDevice; import android.view.InputEvent; import android.view.MotionEvent; /** * InjectionManager is main class for injection InputEvents. * <p> * The class uses internal APIs to inject. Injection depends on android.permission.INJECT_EVENTS. * * @author Anders Bo Pedersen, ABP Consult Aps, 2013 */ public class InjectionManager { private static final String TAG = InjectionManager.class.getSimpleName(); private static int INJECT_INPUT_EVENT_MODE_ASYNC; private static int INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT; private static int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH; private static final String INTERNAL_SERVICE_PRE_JELLY = "window"; private IBinder mWmbinder; private IWindowManager mWinMan; private Object mInputManagerInternal; private Object mInputManager; private Method mInjectEventMethod; public InjectionManager(Context c) { if(Integer.valueOf(android.os.Build.VERSION.SDK_INT) < android.os.Build.VERSION_CODES.JELLY_BEAN) { mWmbinder = ServiceManager.getService( INTERNAL_SERVICE_PRE_JELLY ); mWinMan = IWindowManager.Stub.asInterface( mWmbinder ); printDeclaredMethods(mWinMan.getClass()); //TODO: Implement full injection support for pre Jelly Bean solutions } else { mInputManager = c.getSystemService(Context.INPUT_SERVICE); try { //printDeclaredMethods(mInputManager.getClass()); //Unveil hidden methods mInjectEventMethod = mInputManager.getClass().getDeclaredMethod("injectInputEvent", new Class[] { InputEvent.class, Integer.TYPE }); mInjectEventMethod.setAccessible(true); Field eventAsync = mInputManager.getClass().getDeclaredField("INJECT_INPUT_EVENT_MODE_ASYNC"); Field eventResult = mInputManager.getClass().getDeclaredField("INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT"); Field eventFinish = mInputManager.getClass().getDeclaredField("INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH"); eventAsync.setAccessible(true); eventResult.setAccessible(true); eventFinish.setAccessible(true); INJECT_INPUT_EVENT_MODE_ASYNC = eventAsync.getInt(mInputManager.getClass()); INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT = eventResult.getInt(mInputManager.getClass()); INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = eventFinish.getInt(mInputManager.getClass()); } catch (NoSuchMethodException nsme) { Log.e(TAG, "Critical methods not available"); } catch (NoSuchFieldException nsfe) { Log.e(TAG, "Critical fields not available"); } catch (IllegalAccessException iae) { Log.e(TAG, "Critical fields not accessable"); } } } public void injectTouchEventDown(int x, int y) { MotionEvent me = MotionEvent.obtain( SystemClock.uptimeMillis(), SystemClock.uptimeMillis()+10, MotionEvent.ACTION_DOWN, x, y, 0 ); if(Integer.valueOf(android.os.Build.VERSION.SDK_INT) < android.os.Build.VERSION_CODES.JELLY_BEAN) ; else me.setSource(InputDevice.SOURCE_TOUCHSCREEN); injectEvent(me, INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT); me.recycle(); } public void injectTouchEventRelease(int x, int y) { MotionEvent me = MotionEvent.obtain( SystemClock.uptimeMillis(), SystemClock.uptimeMillis()+10, MotionEvent.ACTION_UP, x, y, 0 ); if(Integer.valueOf(android.os.Build.VERSION.SDK_INT) < android.os.Build.VERSION_CODES.JELLY_BEAN) ; else me.setSource(InputDevice.SOURCE_TOUCHSCREEN); injectEvent(me, INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT); me.recycle(); } private void injectEvent(InputEvent ie, int mode) { try { if(Integer.valueOf(android.os.Build.VERSION.SDK_INT) < android.os.Build.VERSION_CODES.JELLY_BEAN) ; else mInjectEventMethod.invoke(mInputManager, new Object[] { ie, mode }); } catch (IllegalAccessException iae) { Log.e(TAG, "Critical methods not accessable: "+iae.getLocalizedMessage()); } catch (InvocationTargetException ite) { Log.e(TAG, "Error invoking injection method: "+ite.getLocalizedMessage()); } catch (Exception e) { Log.e(TAG, "Error using injection method: "+e.getLocalizedMessage()); } } private void printDeclaredMethods(Class c) { Method[] methods = c.getDeclaredMethods(); for(Method m : methods) { Log.d(TAG, "InputManager method: "+m.getName()); } } }